package com.gmp.thewizard;

import java.util.Arrays;
import java.util.Random;
import android.app.DialogFragment;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Point;
import android.graphics.PointF;
import android.os.CountDownTimer;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

public class Panel extends View {
	
	private static final boolean TEST = false;
	private static final boolean BOUNCE = false;
	private boolean FIRST = true;

	private Avatar avatar;
	private State state;
	private DrawEngine engine;
	private static final String TAG = "Panel";
	protected CountDownTimer counter;
	
	public Panel(Context context){
		super(context);
		this.setId(R.id.panel_view_id);
	    this.state = new State();
		this.avatar = new Avatar(state.avatarspec);
		this.engine = new DrawEngine();
		this.counter = this.createCountDown();
	    setFocusable(true);
	}
	
	protected CountDownTimer createCountDown(){
		return new CountDownTimer(5000, 1000) {
			
			@Override
			public void onTick(long millisUntilFinished) {
				// TODO Auto-generated method stub
				
			}
			
			@Override
			public void onFinish() {
				DialogFragment fragment = FailAlertFragment.newInstance(R.string.app_name);
				fragment.show(((GameActivity) getContext()).getFragmentManager(), "Fail");
			}
		};
	}
	
	protected boolean stopCountDown(){
		if(this.counter != null){
			this.counter.cancel();
			return true;
		} else
			return false;
	}
	
	protected void onSizeChanged(int w, int h, int oldw, int oldh){
		super.onSizeChanged(w, h, oldw, oldh);
		state.setScreenSize(w, h);
		state.obspos = makeGrid(20, 10);
		Log.v(TAG, "Screen size changed: w(" + w + ") h(" + h + ")");
	}
	
	protected void onDraw(Canvas canvas){
		//super.onDraw(canvas);
		canvas.drawColor(Color.WHITE); // background colour
		if(GameActivity.DEBUG) Log.d(TAG, "in onDraw()");
		engine.draw(getContext(), canvas, state.obspos);
		avatar.draw(canvas);
	}
	
	public boolean onTouchEvent(MotionEvent event){
		if(GameActivity.DEBUG) Log.d(TAG, "in onTouchEvent()");
		switch(event.getAction()){
			case MotionEvent.ACTION_DOWN:
				avatar.handleActionDown(event.getX(), event.getY());
				if(FIRST){
					this.counter.start();
					this.FIRST = false;
				}
				if(GameActivity.DEBUG) Log.d(TAG, "Coords: x=" + event.getX() + ", y=" + event.getY());
				break;
			case MotionEvent.ACTION_UP:
				if(avatar.touched)
					avatar.touched = false;
				break;
			case MotionEvent.ACTION_MOVE:
				if(avatar.touched){
					if(obsCollision(event.getX(), event.getY(), state.obspos)){
						if(GameActivity.DEBUG) Log.d(TAG, "Collision: x=" + event.getX() + ", y=" + event.getY());
						changePath();
						// make the avatar bounce...
						if(BOUNCE){
							PointF bounce = this.bounce();
						}
						avatar.touched = false;
						avatar.setSpec(state.avatarspec);
					} else if(borCollision(event.getX(), event.getY(), state.avatarspec.radius)){
						if(state.avatarspec.y < state.avatarspec.radius + 5){
							if(!this.stopCountDown())
								System.out.println("panel: COUNTER FAILED!");
							DialogFragment fragment = WinAlertFragment.newInstance(R.string.app_name);
							fragment.show(((GameActivity) getContext()).getFragmentManager(), "Win");
						}
						avatar.touched = false;
					} else {
						state.setAvatarPos(event.getX(), event.getY());
						avatar.setSpec(state.avatarspec);
					}
					invalidate(); // force callback onDraw()
				}
				break;
			}
		return true;
	}
	
	protected boolean obsCollision(float curx, float cury, Obstacle[] obsposition){
		boolean collision = false;
		if(obsposition.length != 0 && !TEST){
			float sep = obsposition[0].radius * 2;
			for(int x = 0; x < obsposition.length; x++){
				// square collision detection
				//if((position[x][0] + sep >= cury && cury >= position[x][0] - sep) && (position[x][1] + sep >= curx && curx >= position[x][1] - sep))
				//	collision = true;
				// circular collision detection
				float dx = curx - obsposition[x].x;
				float dy = cury - obsposition[x].y;
				if(sep * sep > (dx * dx + dy * dy)){
					state.collision = new PointF(dx, dy);
					collision = true;
					break;
				}
			}
		}
		return collision;
	}
	
	protected boolean borCollision(float curx, float cury, float radius){
		boolean collision = false;
		if((radius >= curx || curx >= state.width - radius) || (radius >= cury || cury >= state.height - radius)){
			if(GameActivity.DEBUG) Log.d(TAG, "Border Collision detected");
			collision = true;
		}
		return collision;
	}
	
	protected PointF bounce(){
		return new PointF(state.collision.x + state.avatarspec.x, state.collision.y + state.avatarspec.y);
	}
	
	protected Obstacle[] makeRow(float offset, float count, float heightoffset){
		if(GameActivity.DEBUG) Log.d(TAG,"in makeRow()");
		Obstacle[] position = new Obstacle[0];
		
		float diao = state.width / count;
		float radius = ((diao - offset) / 2);
		if(GameActivity.DEBUG) Log.d(TAG,"in radius: " + radius);
		if(GameActivity.DEBUG) Log.d(TAG,"in diao: " + diao);
		
		int size = (int)(state.width / diao);
		int half = (int)(offset / 2);
		
		for(int i = 0; i < size; i++){
			float x = half + radius + (diao * i);
			float y = heightoffset + (radius * 2);
			if(this.state.path.contains((int)x, (int)y, (int)state.avatarspec.radius)){
				continue;
			} else
				position = addArray(position, new Obstacle[]{new Obstacle(x, y, radius)});
		}
		return position;
	}
	
	protected Obstacle[] makeGrid(float offset, float count){
		// create enough rows of obstacles to fill the screen.
		// use avatar position to leave a space.
		// space needed per obj: x/2 - r - r - x/2, where r is the radius and x the separation distance
		// x = 30 because r = 25 => 50 diameter, and r*2+30 = 80. 480 / 80 = 6.
		// hence, ...
		if(GameActivity.DEBUG) Log.d(TAG,"in makeGrid()");
		Obstacle[] position = new Obstacle[0];
		float screenx = state.width;
		float screeny = state.height;
		float diao = screenx / count;
		float size = screeny / diao;
		if(GameActivity.DEBUG) Log.d(TAG,"in diao: " + diao + ", size: " + size);
		for(int i = 0; i < size - 1; i++){
			position = addArray(position, makeRow(offset, count, i * diao));
		}
		position = addArray(position, makeRow(offset, count, screeny - (offset * 2)));
		if(GameActivity.DEBUG) Log.d(TAG,"Positions: " + Arrays.deepToString(position));
		return position;
	}
	
	protected Obstacle[] addArray(Obstacle[] first, Obstacle[] ...fs){
		if(GameActivity.DEBUG) Log.d(TAG, "in addArray()");
		int totalsize = first.length;
		if(GameActivity.DEBUG) Log.d(TAG, "size of first: " + totalsize);
		for(Obstacle[] array : fs){
			if(GameActivity.DEBUG) Log.d(TAG, "size of array: " + array.length);
			totalsize += array.length;
		}
		Obstacle[] position = Arrays.copyOf(first, totalsize);
		int offset = first.length;
		for(Obstacle[] array : fs){
			System.arraycopy(array, 0, position, offset, array.length);
			offset += array.length;
		}
		return position;
	}
	
	protected void changePath(){
		Point[] segment = this.state.path.locate((int)state.avatarspec.x, (int)state.avatarspec.y);
		if(segment.length == 0)
			Log.e(TAG, "No Path Segment was Found");
		else {
			// x-axis point generation
			Random rand = new Random();
			int min = (int) this.state.avatarspec.radius;
			int max = (int) (this.state.width - this.state.avatarspec.radius + 1);
			
			if(GameActivity.DEBUG) Log.d(TAG, "in changePath(): avatar: x" + state.avatarspec.x + " y" + state.avatarspec.y + " line: " + segment[0].x + "," + segment[0].y + " - " + segment[1].x + "," + segment[1].y);
			
			// adding the current path on which the avatar is
			this.state.path.add(Arrays.asList(segment));
			this.state.path.add((int)state.avatarspec.x, (int)state.avatarspec.y);
			
			if(!(segment[1].y > this.state.height - this.state.avatarspec.radius)){
				for(int g = segment[1].y; g <= this.state.height; g += this.state.avatarspec.radius * 4){
					this.state.path.add(rand.nextInt(max - min) + min, g);
				}
			}
			if(!(segment[0].y < this.state.avatarspec.radius)){
				for(int g = 0; g < segment[0].y; g += this.state.avatarspec.radius * 4){
					this.state.path.add(rand.nextInt(max - min) + min, g);
				}
			}
			this.state.path.close();

			state.obspos = makeGrid(20, 10);
		}
	}
}
